<?php
// +----------------------------------------------------------------------
// | 前端附件上传处理类, 只针对web/api直接上传文件
// +----------------------------------------------------------------------
namespace app\attachment\controller;

use app\attachment\model\Attachment as Attachment_Model;
use app\common\controller\Base;
use think\Db;

class ApiUpload extends Base
{
    //上传模块
    public $module = 'api';
    private $uploadUrl = '';
    private $uploadPath = '';
    private $uploadDriver = '';
    private $domain = '';

    protected function initialize()
    {
        parent::initialize();
        $this->uploadUrl    = config('public_url') . 'uploads/';
        $this->uploadPath   = config('upload_path');
        $this->uploadDriver = config('upload_driver_v2')['key'];
        $url                = '';
        if ($this->uploadDriver != 'local') {
            $oss = Db::name('third_oss')->field('id, url, cdn')->where('key', $this->uploadDriver)->find();
            if ($oss) {
                $url = !empty($oss['cdn']) ? $oss['cdn'] : $oss['url'];
            }
        }
        $this->domain = $url;
    }

    public function upload($file)
    {
        if ($file == null) {
            return [
                'code' => -1,
                'info' => '获取不到文件信息',
            ];
        }

        // 判断附件是否已存在, 存在直接返回数据
        $file_exists = Db::name('Attachment')
            ->where('md5', '=', $file->hash('md5'))
            ->where('driver', '=', $this->uploadDriver)
            ->find();
        if ($file_exists) {
            return [
                'code'  => 0,
                'info'  => $file_exists['name'] . '上传成功',
                'class' => 'success',
                'id'    => $file_exists['id'],
                'path'  => $this->domain . $file_exists['path'],
            ];
        } else {
            if ($this->uploadDriver == 'local') {
                return $this->saveFile($file);
            } else {
                $driver = Db::name('third_oss')->field('accessKey, secretKey, bucket, endpoint')->where('key', '=', $this->uploadDriver)->findOrEmpty();
                if (!$driver) {
                    return json(['code' => -1, 'info' => '初始化第三方存储失败']);
                }

                $info         = $file->getInfo();
                $file_name    = $file->getInfo('name');
                $ext          = strtolower(substr($file_name, strrpos($file_name, '.') + 1));
                $info['ext']  = $ext;
                $info['md5']  = $file->hash('md5');
                $info['sha1'] = $file->hash('sha1');
                switch ($this->uploadDriver) {
                    //为了方便，oss储存不做太多判断
                    case 'aliyuncs':
                        $aliyun = new AliyuncsUpload($driver);
                        $res    = $aliyun->upload($info);
                        break;
                    case 'qiniu':
                        $qiniu = new QiniuUpload($driver);
                        $res   = $qiniu->upload($info);
                        break;
                    case 'tencent':
                        $tencent = new TencentUpload($driver);
                        $res     = $tencent->upload($info);
                        break;
                    case 'douyin':
                        $douyin = new DouyinUpload($driver);
                        $res    = $douyin->upload($info);
                        break;
                }
                if ($res['code'] == 0) {
                    $info['path']   = $res['url'];
                    $info['module'] = 'api';
                    return $this->save_data($info);
                } else {
                    return ['code' => -1, 'info' => '上传失败', 'state' => '上传失败'];
                }
            }
        }
    }

    /**
     * 本地保存附件
     */
    protected function saveFile($file = null, $dir = 'images')
    {
        // 附件大小限制
        $size_limit = $dir == 'images' ? config('upload_image_size') : config('upload_file_size');
        $size_limit = $size_limit * 1024;
        // 附件类型限制
        $ext_limit = $dir == 'images' ? config('upload_image_ext') : config('upload_file_ext');
        $ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';

        // 判断附件大小是否超过限制
        if ($size_limit > 0 && ($file->getInfo('size') > $size_limit)) {
            return [
                'status' => 0,
                'info'   => '附件过大',
            ];
        }
        // 判断附件格式是否符合
        $file_name = $file->getInfo('name');
        $file_ext  = strtolower(substr($file_name, strrpos($file_name, '.') + 1));
        $error_msg = '';
        if ($ext_limit == '') {
            $error_msg = '获取文件后缀限制信息失败！';
        }
        try {
            $fileMine = $file->getMime();
        } catch (\Exception $ex) {
            $error_msg = $ex->getMessage();
        }
        if ($fileMine == 'text/x-php' || $fileMine == 'text/html') {
            $error_msg = '禁止上传非法文件！';
        }
        if (preg_grep("/php/i", $ext_limit)) {
            $error_msg = '禁止上传非法文件！';
        }
        if (!preg_grep("/$file_ext/i", $ext_limit)) {
            $error_msg = '附件类型不正确！';
        }

        if (!in_array($file_ext, $ext_limit)) {
            $error_msg = '附件类型不正确！';
        }
        if ($error_msg != '') {
            return [
                'code' => -1,
                'info' => $error_msg,
            ];
        }
        // 移动到框架应用根目录指定目录下
        $info = $file->move($this->uploadPath . DIRECTORY_SEPARATOR . $dir);
        if ($info) {
            //判断是否有缩略图路径，没有则创建
            $thumb_dir = './uploads/' . $dir . '/thumb/' . explode('/', str_replace('\\', '/', $info->getSaveName()))[0];
            if (!file_exists($thumb_dir)) {
                mkdir($thumb_dir, 0777, true);
            }

            //压缩比例
            $imageSize = intval($info->getSize());
            $M         = 1024 * 1024;   // 1M大小
            switch ($imageSize) {
                case $imageSize > $M * 4:
                    $percent = 0.1;
                    break;
                case $imageSize > $M * 2:
                    $percent = 0.2;
                    break;
                case $imageSize > $M * 1:
                    $percent = 0.4;
                    break;
                case $imageSize > $M * 0.5:
                    $percent = 0.5;
                    break;
                case $imageSize > $M * 0.3:
                    $percent = 0.7;
                    break;
                case $imageSize > $M * 0.2:
                    $percent = 0.9;
                    break;
                default:
                    $percent = 1;
            }

            $path_prefix = './uploads/';    //文件前缀
            $path        = $dir . '/' . str_replace('\\', '/', $info->getSaveName());  //原图路径
            $thumb_path  = $dir . '/thumb/' . str_replace('\\', '/', $info->getSaveName());  //缩略图路径

            // 生成缩略图
            if (intval($info->getSize()) > 1024 * 512) {
                createThumbImage($path_prefix . $path, $path_prefix . $thumb_path, $percent);
            } else {
                createThumbImage($path_prefix . $path, $path_prefix . $thumb_path);
            }

            // 获取附件信息
            $file_info = [
                'aid'    => 1,
                'uid'    => 0,
                'name'   => $file->getInfo('name'),
                'type'   => $file->getInfo('type'),
                'path'   => $path,
                'thumb'  => $thumb_path,
                'ext'    => $info->getExtension(),
                'size'   => $info->getSize(),
                'md5'    => $info->hash('md5'),
                'sha1'   => $info->hash('sha1'),
                'module' => 'api',
                'driver' => 'local'
            ];
            return $this->save_data($file_info);
        } else {
            return [
                'code' => -1,
                'info' => $file->getError()
            ];
        }
    }

    /**
     * 保存数据到数据库
     * @param $info
     * @return array
     */
    private function save_data($info)
    {
        // 获取附件信息
        $file_info = [
            'aid'    => 1,
            'uid'    => 0,
            'name'   => $info['name'],
            'mime'   => $info['type'],
            'path'   => $info['path'],
            'thumb'  => !empty($info['thumb']) ? $info['thumb'] : '',
            'ext'    => $info['ext'],
            'size'   => $info['size'],
            'md5'    => $info['md5'],
            'sha1'   => $info['sha1'],
            'module' => $info['module'],
            'driver' => $this->uploadDriver
        ];

        if ($file_add = Attachment_Model::create($file_info)) {
            $res = [
                'code' => 0,
                'info' => $file_info['name'] . '上传成功',
                'id'   => intval($file_add['id']),
                'path' => $this->domain . $info['path'],
            ];
        } else {
            $res = [
                'code' => 0,
                'info' => '上传成功,写入数据库失败'
            ];
        }
        return $res;
    }

    /**
     * 上传本地文件到第三方存储
     * @param string $filename
     * @param string $filepath
     * @param string $file
     * @return string
     */
    public function upload_local_file(string $filename, string $filepath, string $file)
    {
        $oss_file = '';
        try {
            $driver = Db::name('third_oss')->field('accessKey, secretKey, bucket, endpoint')->where('key', '=', $this->uploadDriver)->find();
            if (!$driver) {
                throw new \Exception('未配置上传驱动');
            }
            switch ($this->uploadDriver) {
                case 'aliyuncs':
                    $aliyun   = new AliyuncsUpload($driver);
                    $oss_file = $aliyun->upload_local($filename, $filepath, $file);
                    break;
                case 'tencent':
                    $tencent  = new TencentUpload($driver);
                    $oss_file = $tencent->upload_local($filename, $filepath, $file);
                    break;
                case 'douyin':
                    $douyin   = new DouyinUpload($driver);
                    $oss_file = $douyin->upload_local($filename, $filepath, $file);
                    break;
            }
        } catch (\Exception $e) {
            \util\Logger::error('上传失败' . $e->getMessage());
        }
        return $oss_file ? get_file_path($oss_file, 1, 0, $this->uploadDriver) : '';
    }

}
